<?php

	class CustomException extends Exception
	{
		public function __construct($message, $code)
		{
			parent::__construct($message, $code);
		}
	}

	function inscription(&$p_bdd, &$p_xmlData)
	{
		if(array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur est déjà connecté', 1);

		$login = array_key_exists('login', $_POST) ? $_POST['login'] : '';
		$password = array_key_exists('password', $_POST) ? $_POST['password'] : '';
		$email = array_key_exists('email', $_POST) ? $_POST['email'] : '';

		if(empty($login) || empty($password) || empty($email))
			throw new CustomException('Login, password ou email incorrect', 2);

		$stat = $p_bdd->prepare('SELECT id FROM utilisateur WHERE login = :login');
		$stat->bindValue(':login', $login);
		$stat->execute();
		if($stat->rowCount() == 1)
			throw new CustomException('Login déjà attribué', 3);
		$stat->closeCursor();

		$stat = $p_bdd->prepare('INSERT INTO utilisateur (login, password, email) VALUES (:login, SHA1(:password), :email)');
		$stat->bindValue(':login', $login);
		$stat->bindValue(':password', $password);
		$stat->bindValue(':email', $email);
		$stat->execute();
	}

	function listeOuvragesClassement(&$p_bdd, &$p_xmlData)
	{
		$stat = $p_bdd->prepare(
'SELECT
	ouvrage.id AS id,
	ouvrage.titre AS titre,
	ouvrage.auteur AS auteur,
	ouvrage.annee AS annee,
	ouvrage.resume AS resume,
	ouvrage.genreId AS genreId,
	genre.nom AS genre
FROM
	ouvrage, genre, ouvrageClassement
WHERE
	ouvrage.genreId = genre.id AND
	ouvrage.id = ouvrageClassement.ouvrageId'
		);
		$stat->execute();

		$xmlOuvrages = $p_xmlData->addChild('ouvrages');
		while($data = $stat->fetch(PDO::FETCH_ASSOC))
		{
			$xmlOuvrage = $xmlOuvrages->addChild('ouvrage');
			$xmlOuvrage['id'] = $data['id'];
			$xmlOuvrage['genreId'] = $data['genreId'];
			$xmlOuvrage->addChild('titre', $data['titre']);
			$xmlOuvrage->addChild('auteur', $data['auteur']);
			$xmlOuvrage->addChild('genre', $data['genre']);

			if(!empty($data['annee']))
				$xmlOuvrage->addChild('annee', $data['annee']);
			if(!empty($data['resume']))
				$xmlOuvrage->addChild('resume', $data['resume']);
		}

		$stat->closeCursor();
	}

	function classementUtilisateur(&$p_bdd, &$p_xmlData)
	{
		if(array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur est déjà connecté', 1);

		$stat = $p_bdd->prepare('SELECT ouvrageId FROM ouvrageClassement');
		$stat->execute();

		$ouvragesId = array();
		while($data = $stat->fetch(PDO::FETCH_ASSOC))
			$ouvragesId[] = $data['ouvrageId'];

		$stat->closeCursor();

		$ouvragesNb = count($ouvragesId);
		$choix = array();
		for($i = 0 ; $i < $ouvragesNb ; ++$i)
		{
			if(!array_key_exists('choix' . $i, $_POST))
				throw new CustomException('Un ou plusieurs choix manquants', 2);

			$currentChoix = $_POST['choix' . $i];
			if(!in_array($currentChoix, $ouvragesId))
				throw new CustomException('Un id d\'ouvrage invalide au moins', 3);
			else if(in_array($currentChoix, $choix))
				throw new CustomException('Id d\'ouvrage utilisé deux fois', -1);

			$choix[] = $currentChoix;
		}

		$stat = $p_bdd->prepare(
'INSERT INTO classement (
	utilisateurId,
	ouvrageClassementId,
	ordre
) VALUES (
	:utilisateurId,
	:ouvrageClassementId,
	:ordre
)'
		);

		for($i = 0 ; $i < $ouvragesNb ; ++$i)
			$stat->execute(array(
				':utilisateurId'		=> $_SESSION['utilisateur'],
				':ouvrageClassementId'	=> $choix[$i],
				':ordre'				=> $i
			));
	}

	function connexion(&$p_bdd, &$p_xmlData)
	{
		// if(array_key_exists('utilisateur', $_SESSION))
			// throw new CustomException('L\'utilisateur est déjà connecté', 1);

		$login = array_key_exists('login', $_POST) ? $_POST['login'] : '';
		$password = array_key_exists('password', $_POST) ? $_POST['password'] : '';
		$resterConnecte  = array_key_exists('resterConnecte ', $_POST) ? $_POST['resterConnecte '] : '';

		if(empty($login))
		{
			if(!array_key_exists('utilisateur', $_SESSION))
				throw new CustomException('Echec de récupération de session', 2);
		}
		else
		{
			$stat = $p_bdd->prepare('SELECT id FROM utilisateur WHERE login = :login AND password = SHA1(:password)');
			$stat->bindValue(':login', $login);
			$stat->bindValue(':password', $password);
			$stat->execute();
			if($stat->rowCount() == 0)
				throw new CustomException('Le login et le mot de passe spécifiés ne permettent pas la connexion', 3);

			$res = $stat->fetch(PDO::FETCH_ASSOC);
			$_SESSION['utilisateur'] = $res['id'];
			$_SESSION['resterConnecte'] = $resterConnecte;
			if($resterConnecte)
				setcookie(session_name(), $_COOKIE[session_name()], time() + 3600 * 24 * 10);

			$stat->closeCursor();
		}
	}

	function deconnexion(&$p_bdd, &$p_xmlData)
	{
		if(!array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur n\'est pas connecté', 1);

		if(array_key_exists('resterConnecte', $_SESSION) && $_SESSION['resterConnecte'])
			setcookie(session_name(), '', 0);

		session_unset();
	}

	function listeOuvrages(&$p_bdd, &$p_xmlData)
	{
		$titre = array_key_exists('titre', $_POST) ? $_POST['titre'] : '';
		$auteur = array_key_exists('auteur', $_POST) ? $_POST['auteur'] : '';
		$annee = array_key_exists('annee', $_POST) ? $_POST['annee'] : '';
		$tri = (array_key_exists('tri', $_POST) && in_array(array(), $_POST['tri'])) ? $_POST['tri'] : 'titre';
		$triSens = (array_key_exists('triSens', $_POST) && $_POST['triSens'] == 'croissant') ? 'ASC' : 'DESC';
		$paginationDepart = (array_key_exists('paginationDepart', $_POST) && is_numeric($_POST['paginationDepart'])) ? $_POST['paginationDepart'] : 0;
		$paginationTaille = (array_key_exists('paginationTaille', $_POST) && is_numeric($_POST['paginationTaille'])) ? $_POST['paginationTaille'] : 10;

		$req =
'SELECT
	ouvrage.id AS id,
	ouvrage.titre AS titre,
	ouvrage.auteur AS auteur,
	ouvrage.annee AS annee,
	ouvrage.resume AS resume,
	ouvrage.genreId AS genreId,
	genre.nom AS genre
FROM ouvrage, genre
WHERE
	ouvrage.genreId = genre.id';

		if(array_key_exists('utilisateur', $_SESSION))
			$req =
'SELECT
	ouvrage.id AS id,
	ouvrage.titre AS titre,
	ouvrage.auteur AS auteur,
	ouvrage.annee AS annee,
	ouvrage.resume AS resume,
	ouvrage.genreId AS genreId,
	ouvrage.genre AS genre,
	note.note AS note
FROM (' . $req . ') ouvrage
LEFT JOIN (
	SELECT ouvrageId, note
	FROM note
	WHERE utilisateurId = :utilisateurId
) note ON id = note.ouvrageId
WHERE
	titre != \'\'';

		if(!empty($titre))		$req .= ' AND UPPER(titre) LIKE UPPER(:titre)';
		if(!empty($auteur))		$req .= ' AND UPPER(auteur) LIKE UPPER(:auteur)';
		if(!empty($annee))		$req .= ' AND annee = :annee';
		$req .= ' ORDER BY ' . $tri . ' ' . $triSens;
		$req .= ' LIMIT ' . $paginationDepart . ',' . $paginationTaille;

		$stat = $p_bdd->prepare($req);
		if(array_key_exists('utilisateur', $_SESSION))
			$stat->bindValue(':utilisateurId', $_SESSION['utilisateur']);
		if(!empty($titre))		$stat->bindValue(':titre', '%' . $titre . '%');
		if(!empty($auteur))		$stat->bindValue(':auteur', '%' . $auteur . '%');
		if(!empty($annee))		$stat->bindValue(':annee', $annee);

		$xmlOuvrages = $p_xmlData->addChild('ouvrages');
		$stat->execute();

		while($data = $stat->fetch(PDO::FETCH_ASSOC))
		{
			$xmlOuvrage = $xmlOuvrages->addChild('ouvrage');
			$xmlOuvrage['id'] = $data['id'];
			$xmlOuvrage['genreId'] = $data['genreId'];
			$xmlOuvrage->titre = $data['titre'];
			$xmlOuvrage->auteur = $data['auteur'];
			$xmlOuvrage->genre = $data['genre'];

			if(array_key_exists('utilisateur', $_SESSION))
				$xmlOuvrage->note = (empty($data['note'])) ? 0 : $data['note'];

			if(!is_null($data['annee']))	$xmlOuvrage->annee = $data['annee'];
			if(!is_null($data['resume']))	$xmlOuvrage->resume = $data['resume'];
		}

		$stat->closeCursor();
	}
	
	function listeNotes(&$p_bdd, &$p_xmlData)
	{
		if(!array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur n\'est pas connecté', 1);
			
		$titre = array_key_exists('titre', $_POST) ? $_POST['titre'] : '';
		$auteur = array_key_exists('auteur', $_POST) ? $_POST['auteur'] : '';
		$annee = array_key_exists('annee', $_POST) ? $_POST['annee'] : '';
		$tri = (array_key_exists('tri', $_POST) && in_array(array(), $_POST['tri'])) ? $_POST['tri'] : 'titre';
		$triSens = (array_key_exists('triSens', $_POST) && $_POST['triSens'] == 'croissant') ? 'ASC' : 'DESC';
		$paginationDepart = (array_key_exists('paginationDepart', $_POST) && is_numeric($_POST['paginationDepart'])) ? $_POST['paginationDepart'] : 0;
		$paginationTaille = (array_key_exists('paginationTaille', $_POST) && is_numeric($_POST['paginationTaille'])) ? $_POST['paginationTaille'] : 10;

		$req =
'SELECT
	ouvrage.id AS id,
	ouvrage.titre AS titre,
	ouvrage.auteur AS auteur,
	ouvrage.annee AS annee,
	ouvrage.resume AS resume,
	ouvrage.genreId AS genreId,
	genre.nom AS genre,
	note.note AS note
FROM ouvrage, genre, note
WHERE
	ouvrage.genreId = genre.id AND
	ouvrage.id = note.ouvrageId AND
	note.utilisateurId = :utilisateurId';

		if(!empty($titre))		$req .= ' AND titre = :titre';
		if(!empty($auteur))		$req .= ' AND auteur = :auteur';
		if(!empty($annee))		$req .= ' AND annee = :annee';
		$req .= ' ORDER BY ' . $tri . ' ' . $triSens;
		$req .= ' LIMIT ' . $paginationDepart . ',' . $paginationTaille;

		$stat = $p_bdd->prepare($req);
		if(!empty($titre))		$stat->bindValue(':titre', $titre);
		if(!empty($auteur))		$stat->bindValue(':auteur', $auteur);
		if(!empty($annee))		$stat->bindValue(':annee', $annee);

		$xmlOuvrages = $p_xmlData->addChild('ouvrages');
		$stat->bindValue(':utilisateurId', $_SESSION['utilisateur']);
		$stat->execute();

		while($data = $stat->fetch(PDO::FETCH_ASSOC))
		{
			$xmlOuvrage = $xmlOuvrages->addChild('ouvrage');
			$xmlOuvrage['id'] = $data['id'];
			$xmlOuvrage['genreId'] = $data['genreId'];
			$xmlOuvrage->titre = $data['titre'];
			$xmlOuvrage->auteur = $data['auteur'];
			$xmlOuvrage->genre = $data['genre'];
			$xmlOuvrage->note = $data['note'];

			if(!is_null($data['annee']))	$xmlOuvrage->annee = $data['annee'];
			if(!is_null($data['resume']))	$xmlOuvrage->resume = $data['resume'];
		}

		$stat->closeCursor();
	}
	
	function listeGenres(&$p_bdd, &$p_xmlData)
	{
		$stat = $p_bdd->prepare('select id, nom from genre');
		$stat->execute();
		
		$xmlGenres = $p_xmlData->addChild('genres');
		while($data = $stat->fetch(PDO::FETCH_ASSOC))
		{
			$xmlGenre = $xmlGenres->addChild('genre', $data['nom']);
			$xmlGenre['id'] = $data['id'];
		}
	}

	function ajoutOuvrage(&$p_bdd, &$p_xmlData)
	{
		if(!array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur n\'est pas connecté', 1);

		$titre = array_key_exists('titre', $_POST) ? $_POST['titre'] : '';
		$auteur = array_key_exists('auteur', $_POST) ? $_POST['auteur'] : '';
		$annee = array_key_exists('annee', $_POST) ? $_POST['annee'] : '';
		$resume = array_key_exists('resume', $_POST) ? $_POST['resume'] : '';
		$genreId = array_key_exists('genreId', $_POST) ? $_POST['genreId'] : '';

		if(empty($titre))
			throw new CustomException('Champ [titre] manquant', -1);
		else if(empty($auteur))
			throw new CustomException('Champ [auteur] manquant', -1);
		else if(empty($genreId))
			throw new CustomException('Champ [genreId] manquant', -1);

		$stat = $p_bdd->prepare(
'INSERT INTO ouvrage (
	titre,
	auteur,
	annee,
	resume,
	genreId
) VALUES (
	:titre,
	:auteur,
	:annee,
	:resume,
	:genreId
)'
		);
		$stat->bindValue(':titre', $titre);
		$stat->bindValue(':auteur', $auteur);
		$stat->bindValue(':annee', $annee);
		$stat->bindValue(':resume', $resume);
		$stat->bindValue(':genreId', $genreId);
		$stat->execute();
	}

	function noteOuvrage(&$p_bdd, &$p_xmlData)
	{
		if(!array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur n\'est pas connecté', 1);

		$ouvrageId = array_key_exists('ouvrageId', $_POST) ? $_POST['ouvrageId'] : '';
		$note = array_key_exists('note', $_POST) ? $_POST['note'] : '';

		if(empty($ouvrageId))
			throw new CustomException('Champ [ouvrageId] manquant', -1);
		else if(empty($note))
			throw new CustomException('Champ [note] manquant', -1);

		$stat = $p_bdd->prepare('REPLACE INTO note (utilisateurId, ouvrageId, note) VALUES (:utilisateurId, :ouvrageId, :note)');
		$stat->bindValue(':utilisateurId', $_SESSION['utilisateur']);
		$stat->bindValue(':ouvrageId', $ouvrageId);
		$stat->bindValue(':note', $note);
		$stat->execute();
	}

	function vectorSort($a, $b)
	{
		if($a[0] == $b[0])
			return 0;
		else if($a[0] < $b[0])
			return -1;
		else
			return 1;
	}

	function recommandationNote(&$p_bdd, &$p_xmlData)
	{
		if(!array_key_exists('utilisateur', $_SESSION))
			throw new CustomException('L\'utilisateur n\'est pas connecté', 1);

		$stat = $p_bdd->prepare('SELECT ouvrageId, note FROM note WHERE utilisateurId = :utilisateurId');
		$stat->bindValue(':utilisateurId', $_SESSION['utilisateur']);
		$stat->execute();

		$vectorReference = array();
		while($data = $stat->fetch(PDO::FETCH_ASSOC))
			$vectorReference[$data['ouvrageId']] = $data['note'];

		$stat->closeCursor();

		$statUtilisateur = $p_bdd->prepare('SELECT id FROM utilisateur WHERE id != :id');
		$statUtilisateur->bindValue(':id', $_SESSION['utilisateur']);
		$statUtilisateur->execute();

		$statNotes = $p_bdd->prepare('SELECT ouvrageId, note FROM note WHERE utilisateurId = :utilisateurId');

		$othersVector = array();
		while($dataUtilisateur = $statUtilisateur->fetch(PDO::FETCH_ASSOC))
		{
			$vector = array();
			$utilisateurId = $dataUtilisateur['id'];

			$statNotes->execute(array(':utilisateurId' => $utilisateurId));
			while($dataNotes = $statNotes->fetch(PDO::FETCH_ASSOC))
				$vector[$dataNotes['ouvrageId']] = $dataNotes['note'];

			$statNotes->closeCursor();
			$othersVector[$utilisateurId] = $vector;
		}

		$statUtilisateur->closeCursor();

		if(count($othersVector) < 3)
			throw new CustomException('Il n\'y a pas assez d\'utilisateurs pour proposer une recommandation', -1);

		$othersCompatibility = array();
		foreach($othersVector as $utilisateurId => $utilisateurVector)
		{
			$numerator = 0;
			$denominator1 = 0;
			$denominator2 = 0;

			foreach($vectorReference as $key => $value)
			{
				if(array_key_exists($key, $utilisateurVector))
					$numerator += $value * $utilisateurVector[$key];

				$denominator1 += $value * $value;
			}

			foreach($utilisateurVector as $key => $value)
				$denominator2 += $value * $value;

			$denominator = sqrt($denominator1) * sqrt($denominator2);
			$angle = ($denominator == 0) ? 180 : acos(abs($numerator) / $denominator);
			$othersCompatibility[] = array($angle, $utilisateurId);
		}

		usort($othersCompatibility, 'vectorSort');

		$stat = $p_bdd->prepare(
// 'SELECT
	// ouvrage.id AS id,
	// ouvrage.titre AS titre,
	// ouvrage.auteur AS auteur,
	// ouvrage.annee AS annee,
	// ouvrage.resume AS resume,
	// ouvrage.genreId AS genreId,
	// genre.nom AS genre
// FROM
	// ouvrage, genre
// WHERE
	// ouvrage.genreId = genre.id AND
	// ouvrage.id IN (
		// SELECT DISTINCT(ouvrageId)
		// FROM note
		// WHERE
			// utilisateurId IN (:u1, :u2, :u3) AND
			// ouvrageId NOT IN (
				// SELECT ouvrageId
				// FROM note
				// WHERE
					// utilisateurId = :utilisateurId
			// ) AND
			// note >= 4
		// ORDER BY note DESC
	// )
// LIMIT 0,5'
'SELECT
	ouvrage.id AS id,
	ouvrage.titre AS titre,
	ouvrage.auteur AS auteur,
	ouvrage.annee AS annee,
	ouvrage.resume AS resume,
	ouvrage.genreId AS genreId,
	genre.nom AS genre
FROM
	ouvrage,
	genre,
	(
		SELECT
			ouvrage.id AS id,
			SUM(note.note) AS note
		FROM ouvrage, note
		WHERE
			ouvrage.id = note.ouvrageId AND
			utilisateurId IN (:u1, :u2, :u3) AND
			note >= 4
		GROUP BY id
	) selection
WHERE
	ouvrage.genreId = genre.id AND
	ouvrage.id = selection.id AND
	ouvrage.id NOT IN (
		SELECT ouvrageId
		FROM note
		WHERE
			utilisateurId = :utilisateurId
	)
ORDER BY selection.note DESC
LIMIT 0,5'
		);

		$stat->bindValue(':u1', $othersCompatibility[0][1]);
		$stat->bindValue(':u2', $othersCompatibility[1][1]);
		$stat->bindValue(':u3', $othersCompatibility[2][1]);
		$stat->bindValue(':utilisateurId', $_SESSION['utilisateur']);
		$stat->execute();

		$xmlOuvrages = $p_xmlData->addChild('ouvrages');
		while($data = $stat->fetch(PDO::FETCH_ASSOC))
		{
			$xmlOuvrage = $xmlOuvrages->addChild('ouvrage');
			$xmlOuvrage['id'] = $data['id'];
			$xmlOuvrage['genreId'] = $data['genreId'];
			$xmlOuvrage->titre = $data['titre'];
			$xmlOuvrage->auteur = $data['auteur'];
			$xmlOuvrage->genre = $data['genre'];

			if(!is_null($data['annee']))	$xmlOuvrage->annee = $data['annee'];
			if(!is_null($data['resume']))	$xmlOuvrage->resume = $data['resume'];
		}

		$stat->closeCursor();
	}

	function handleError($p_num, $p_message, $p_file, $p_line, $p_context)
	{
		throw new Exception($p_message);
	}

	function insertStackTrace(&$p_xml, $p_stackTrace)
	{
		$xmlStackTrace = $p_xml->data->addChild('stackTrace');
		for($i = 0, $end = count($p_stackTrace) ; $i < $end ; ++$i)
		{
			$stackTraceElement = &$p_stackTrace[$i];

			$value = $stackTraceElement['function'] . '() line ' . $stackTraceElement['line'];
			if(array_key_exists('class', $stackTraceElement))
				$value = $stackTraceElement['class'] . '::' . $value;

			$xmlStackTrace->addChild('stackTraceElement', $value);
		}
	}

	error_reporting(E_ALL);
	set_error_handler('handleError');
	header('Content-Type: application/xml');
	session_start();
	if(array_key_exists('resterConnecte', $_SESSION) && $_SESSION['resterConnecte'])
		setcookie(session_name(), $_COOKIE[session_name()], time() + 3600 * 24 * 10);

	$bdd = new PDO('mysql:host=localhost;dbname=jsaispasquoilire', 'root', '');
	$bdd->exec('SET NAMES utf8');
	$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	$bdd->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);

	$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?><message><status></status><data></data></message>');

	try
	{
		$fonction = $_GET['fonction'];
		switch($fonction)
		{
			case 'inscription': 				inscription($bdd, $xml->data);					break;
			case 'listeOuvragesClassement': 	listeOuvragesClassement($bdd, $xml->data);		break;
			case 'classementUtilisateur':	 	classementUtilisateur($bdd, $xml->data);		break;
			case 'connexion':					connexion($bdd, $xml->data);					break;
			case 'deconnexion':					deconnexion($bdd, $xml->data);					break;
			case 'listeOuvrages':				listeOuvrages($bdd, $xml->data);				break;
			case 'listeNotes':					listeNotes($bdd, $xml->data);					break;
			case 'listeGenres':					listeGenres($bdd, $xml->data);					break;
			case 'ajoutOuvrage':				ajoutOuvrage($bdd, $xml->data);					break;
			case 'noteOuvrage':					noteOuvrage($bdd, $xml->data);					break;
			case 'recommandationNote':			recommandationNote($bdd, $xml->data);			break;
			default:
				throw new CustomException('Fonction invalide', 1);
		}

		$xml->status = 0;
	}
	catch(CustomException $ex)
	{
		$xml->status = $ex->getCode();
		unset($xml->data);
		$xml->data->errorMessage = $ex->getMessage();
		insertStackTrace($xml, $ex->getTrace());
	}
	catch(Exception $ex)
	{
		$xml->status = -1;
		unset($xml->data);
		$xml->data->errorMessage = 'Internal error : ' . $ex->getMessage();
		insertStackTrace($xml, $ex->getTrace());
	}

	echo $xml->asXML();

?>